import random
from django import forms
from django.core.validators import RegexValidator, EmailValidator, ValidationError
# from django.core.validators import EmailValidator
# from django.core.exceptions import ValidationError
from django.conf import settings
from django_redis import get_redis_connection
from app01 import models
# from app01.forms.bootstrap import BootStrapForm
from utils.tencent.sms import send_sms_single
from utils import encrypt


class BootStrapForm(forms.Form):  # 基类
    bootstrap_class_exclude = []

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for name, field in self.fields.items():
            if name in self.bootstrap_class_exclude:
                continue
            old_class = field.widget.attrs.get('class', "")
            field.widget.attrs['class'] = '{} form-control'.format(old_class)
            field.widget.attrs['placeholder'] = '请输入%s' % (field.label,)


# 用forms验证,是没有通过数据库表的,自我定义验证的
class LoginFrom(forms.Form):
    username = forms.CharField(label='邮箱或手机号')
    password = forms.CharField(label='密码', widget=forms.PasswordInput(render_value=True))
    code = forms.CharField(label='图片验证码')

    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request
        for key, value in self.fields.items():
            value.widget.attrs['class'] = 'form-control form-select-sm ps-2'
            value.widget.attrs['autocomplete'] = 'on'  # off不显示历史输过记录
            value.widget.attrs[
                'style'] = 'height: 38px; border: 1px solid grey; margin-bottom: 10px; font-size: 16px; color: black;'

    def clean_password(self):
        pwd = self.cleaned_data['password']
        # 加密 & 返回
        return encrypt.md5(pwd)

    def clean_code(self):
        """ 钩子 图片验证码是否正确？ """
        # 读取用户输入的验证码
        code = self.cleaned_data['code']

        # 去session获取自己的验证码
        session_code = self.request.session.get('image_code')
        if not session_code:
            raise ValidationError('验证码已过期，请重新获取')

        if code.strip().upper() != session_code.strip().upper():
            raise ValidationError('验证码输入错误')

        return code


class LoginSMSForm(forms.Form):
    mobile = forms.CharField(
        label='手机号',
        validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ]
    )

    code = forms.CharField(
        label='验证码',
        widget=forms.TextInput())

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for key, value in self.fields.items():
            value.widget.attrs['class'] = 'form-control form-select-sm ps-2'
            # value.widget.attrs['autocomplete'] = 'off'  # 不显示历史输过记录
            value.widget.attrs[
                'style'] = 'height: 38px; border: 1px solid grey; margin-bottom: 10px; font-size: 16px; color: black;'

    def clean_mobile(self):
        mobile = self.cleaned_data['mobile']
        exists = models.AdminCustomer.objects.filter(mobile=mobile).exists()
        # 如果要做用户登录信息记入session，就要给返回这个用户信息对象：user_object
        # user_object = models.AdminCustomer.objects.filter(mobile=mobile).first()
        if not exists:
            raise ValidationError('手机号不存在')

        return mobile  # user_object

    def clean_code(self):
        code = self.cleaned_data['code']
        mobile = self.cleaned_data.get('mobile')

        # 手机号不存在，则验证码无需再校验
        if not mobile:
            return code

        conn = get_redis_connection()
        redis_code = conn.get(mobile)  # 根据手机号去获取验证码
        if not redis_code:
            raise ValidationError('验证码失效或未发送，请重新发送')

        redis_str_code = redis_code.decode('utf-8')

        if code.strip() != redis_str_code:
            raise ValidationError('验证码错误，请重新输入')

        return code


class SendSmsForm(forms.Form):
    mobile = forms.CharField(label='手机号',
                             validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])

    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    def clean_mobile(self):
        """ 手机号校验的钩子 """
        mobile = self.cleaned_data['mobile']
        # 判断短信模板是否有问题
        tpl = self.request.GET.get('tpl')
        template_id = settings.TENCENT_SMS_TEMPLATE.get(tpl)
        if not template_id:
            # self.add_error('mobile_phone','短信模板错误') # 这个还会走下边
            raise ValidationError('短信模板错误')  # 这个就不好走下边了，因为返回去了

        exists = models.AdminCustomer.objects.filter(mobile=mobile).exists()

        if tpl == 'login':
            if not exists:
                raise ValidationError('手机号不存在')
        else:
            # 校验数据库中是否已有手机号
            if exists:
                raise ValidationError('手机号已存在')

        code = random.randrange(1000, 9999)

        # 发送短信,python3.12以后,需要把源码httpclient.py改一下62行处,把encoding="utf=8"去掉,3.12版本已经自己加上了,不要重复出错
        sms = send_sms_single(mobile, template_id, [code, ])

        if sms['result'] != 0:
            raise ValidationError("短信发送失败，{}".format(sms['errmsg']))

        # 验证码 写入redis（django-redis）
        conn = get_redis_connection()
        conn.set(mobile, code, ex=60)
        return mobile


class RegisterFrom(forms.ModelForm):
    username = forms.CharField(
        label='用户名',
        min_length=2,
        max_length=64,
        error_messages={'min_length': "用户名长度不能小于2个字符", 'max_length': "用户名长度不能大于64个字符"},
    )
    email = forms.CharField(
        validators=[EmailValidator(), ],
        error_messages={'invalid': '请输入有效的邮箱地址。'}
    )

    password_repeat = forms.CharField(
        label='重复密码',
        min_length=8,
        max_length=64,
        error_messages={'min_length': "密码长度不能小于8个字符", 'max_length': "密码长度不能大于64个字符"},
        # required=True,  # 这个表示必填，后边验证要用到
        # 密码默认不给保留,非要保留加上:render_value=True。 "required"是浏览器验证，可以不加
        widget=forms.PasswordInput(attrs={"class": "form-control ps-1", "required": ""}))

    password = forms.CharField(
        label='密码',
        min_length=8,
        max_length=64,
        error_messages={'min_length': "密码长度不能小于8个字符", 'max_length': "密码长度不能大于64个字符"},
        # required=True,  # 这个表示必填，后边验证要用到
        # 密码默认不给保留,非要保留加上:render_value=True。 "required"是浏览器验证，可以不加
        widget=forms.PasswordInput(attrs={"class": "form-control ps-1", "required": ""}))

    mobile = forms.CharField(label='手机号',
                             validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])

    code = forms.CharField(
        label='验证码',
        widget=forms.TextInput(attrs={"class": "form-control ps-1"})
    )

    class Meta:
        model = models.AdminCustomer
        fields = ['username', 'email', 'password', 'password_repeat', 'mobile', 'code']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for key, value in self.fields.items():
            # print(key)
            value.widget.attrs['class'] = 'form-control form-select-sm ps-2'
            value.widget.attrs['autocomplete'] = 'on'  # 显示历史输过记录
            value.widget.attrs[
                'style'] = 'height: 38px; border: 1px solid grey; margin-bottom: 10px; font-size: 16px; color: black;'

    def clean_username(self):
        username = self.cleaned_data['username']
        exists = models.AdminCustomer.objects.filter(username=username).exists()
        if exists:
            raise ValidationError('用户名已存在')
            # self.add_error('username','用户名已存在')
        return username

    def clean_email(self):
        email = self.cleaned_data['email']
        exists = models.AdminCustomer.objects.filter(email=email).exists()
        if exists:
            raise ValidationError('邮箱已存在')
        return email

    def clean_password(self):
        pwd = self.cleaned_data['password']
        # 加密 & 返回
        return encrypt.md5(pwd)

    def clean_confirm_password(self):
        pwd = self.cleaned_data.get('password')
        confirm_pwd = encrypt.md5(self.cleaned_data['confirm_password'])
        if pwd != confirm_pwd:
            raise ValidationError('两次密码不一致')
        return confirm_pwd

    def clean_mobile(self):
        mobile = self.cleaned_data['mobile']
        exists = models.AdminCustomer.objects.filter(mobile=mobile).exists()
        if exists:
            raise ValidationError('手机号已注册')
        return mobile

    def clean_code(self):
        code = self.cleaned_data['code']
        mobile = self.cleaned_data.get('mobile')  # 这个取完没有会返回None
        if not mobile:
            return code

        conn = get_redis_connection()
        redis_code = conn.get(mobile)

        if not redis_code:
            raise ValidationError('验证码失效或未发送，请重新发送')

        redis_str_code = redis_code.decode('utf-8')
        if code.strip() != redis_str_code:
            raise ValidationError('验证码错误，请重新输入')

        return code


class ForgotForm(forms.Form):
    email = forms.CharField(
        label='邮箱地址',
        validators=[EmailValidator(), ],
        error_messages={'invalid': '请输入有效的邮箱地址。'},
        widget=forms.EmailInput(attrs={"class": "form-control", "required": "", 'autocomplete': 'on'}),
    )
    code = forms.CharField(
        label='验证码',
        widget=forms.TextInput(attrs={"class": "form-control ps-1", "placeholder": "请输入邮箱验证码"}),
    )

    def clean_email(self):
        email = self.cleaned_data['email']
        exists = models.AdminCustomer.objects.filter(email=email).exists()
        if not exists:
            raise ValidationError('邮箱没注册过')
        return email

    # 这里code字段不进行验证
    def clean(self):
        cleaned_data = super().clean()
        # 移除code字段的验证错误信息
        if 'code' in self._errors:
            del self._errors['code']
        return cleaned_data


class ForgotResetForm(forms.Form):
    email = forms.CharField(
        label='邮箱地址',
        validators=[EmailValidator(), ],
        error_messages={'invalid': '请输入有效的邮箱地址。'},
        widget=forms.EmailInput(attrs={"class": "form-control", "required": ""}),
    )

    code = forms.CharField(
        label='验证码',
        widget=forms.TextInput(attrs={"class": "form-control ps-1"}),
    )

    def clean_email(self):
        email = self.cleaned_data['email']
        exists = models.AdminCustomer.objects.filter(email=email).exists()
        if not exists:
            raise ValidationError('邮箱没注册过')
        return email

    def clean_code(self):
        code = self.cleaned_data['code']
        email = self.cleaned_data.get('email')  # 这个取完没有会返回None
        if not email:
            return code
        conn = get_redis_connection()
        redis_code = conn.get(email)

        if not redis_code:
            raise ValidationError('验证码失效或未获取，请重新获取')

        redis_str_code = redis_code.decode('utf-8')
        if code.strip() != redis_str_code:
            raise ValidationError('验证码错误，请重新输入')

        return code


class ForgotPWDForm(forms.Form):
    password = forms.CharField(
        label='新密码',
        min_length=8,
        max_length=64,
        error_messages={'min_length': "密码长度不能小于8个字符", 'max_length': "密码长度不能大于64个字符"},
        # required=True,  # 这个表示必填，后边验证要用到, autocomplete规定输入字段是否应该启用自动完成功能
        # 密码默认不给保留,非要保留加上:render_value=True。 "required"是浏览器验证，可以不加
        widget=forms.PasswordInput(attrs={"class": "form-control ps-1", "required": "",
                                          'style': 'height: 38px; border: 1px solid grey; margin-bottom: 5px; font-size: 16px;',
                                          'autocomplete': "off"
                                          }))
    password_repeat = forms.CharField(
        label='重复密码',
        min_length=8,
        max_length=64,
        error_messages={'min_length': "密码长度不能小于8个字符", 'max_length': "密码长度不能大于64个字符"},
        # required=True,  # 这个表示必填，后边验证要用到, autocomplete规定输入字段是否应该启用自动完成功能
        # 密码默认不给保留,非要保留加上:render_value=True。 "required"是浏览器验证，可以不加
        widget=forms.PasswordInput(attrs={"class": "form-control ps-1", "required": "",
                                          'style': 'height: 38px; border: 1px solid grey; margin-bottom: 5px; font-size: 16px;',
                                          'autocomplete': "off"
                                          }))

    def clean_password(self):
        pwd = self.cleaned_data['password']
        # 加密 & 返回
        return encrypt.md5(pwd)

    def clean_password_repeat(self):
        pwd = self.cleaned_data.get('password')
        confirm_pwd = encrypt.md5(self.cleaned_data['password_repeat'])
        if pwd != confirm_pwd:
            raise ValidationError('两次密码不一致')
        return confirm_pwd

